package hotel.quartz.oversea.job.switchs;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import hotel.base.oversea.constant.CommonConstants;
import hotel.base.oversea.constant.CommonUrlConstants;
import hotel.base.oversea.constant.SwitchConstants;
import hotel.base.oversea.constant.quartz.UrlSwitchsEnums;
import hotel.base.oversea.utils.Result;
import hotel.quartz.oversea.config.service.RedisService;
import hotel.quartz.oversea.job.base.BaseJob;
import hotel.quartz.oversea.vo.switchs.RoomStaticInfoVo;
import hotel.quartz.oversea.service.JobAndTriggerService;
import hotel.quartz.oversea.util.JobRestTemplateUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  Switch Hotel Room Static Job
 * </p>
 *
 * @description: Switch Hotel Room Static Job
 * @author: qiuyy
 * @date: 2020-05-26 13:22
 * @version: V1.0
 */
@Slf4j
public class SwitchHotelRoomStaticJob implements BaseJob {
    //定位酒店id，标识是否已查询
    private String redisSwHotelChildIdNow = SwitchConstants.REDIS_SWITCH_HOTEL_CHILD_ID_ROOM_STATIC_NOW;
    private String redisSwHotelChildIdsHas = SwitchConstants.REDIS_SWITCH_HOTEL_CHILD_IDS_ROOM_STATIC_HAS;
    private String redisSwHotelChildIds = SwitchConstants.REDIS_SWITCH_HOTEL_CHILD_IDS;
    private String timestamp = "Shrsj"+String.valueOf(System.currentTimeMillis());
    private int thisRedisCount = SwitchConstants.REDIS_SW_BASIC_ROOM_DATA_WAIT_COUNT;
    private String redisWaitSaveKey = SwitchConstants.REDIS_SW_BASIC_ROOM_DATA_WAIT_SAVE;
    private String SwitchHotelRoomStaticJobProcessIngRedisHead = "SwitchHotelRoomStaticJobProcessIngRedisHead";
    //定时器
    private String jobGroupName = SwitchConstants.JOB_GROUP_NAME_SWITCHS;
    private String jobClassName = this.getClass().getName();

    @Autowired
    private JobAndTriggerService jobAndTriggerService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedisService redisService;
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 静态物理房型信息，每6-10秒跑一次
     *
     * 每次上送一个子酒店id+母酒店id
     *
     * 获取和保存分两步，如果数据库没有母物理房型数据，需排查
     *
     * @param jobExecutionContext
     */
    @Override
    @SneakyThrows
    public void execute(JobExecutionContext jobExecutionContext) {
        log.info("Switch Hotel Room Static Job {} >>>>>>>> 携程静态物理房型执行时间: {}", timestamp, DateUtil.now());

       // String host = CommonUrlConstants.SWITCHS_HOST;
        String host= CommonUrlConstants.SWITCHS_ONLINE_HOST;
        String roomMethodGetUrl = host + UrlSwitchsEnums.room.GET_ROOM_STATIC.getValue();
        String saveUrl = host + UrlSwitchsEnums.room.SAVE_ROOM_STATIC.getValue();//+"?access_token="+accessToken;
        boolean check = checkSaveRedisData(saveUrl,false);
        if(!check){
            Map<String,Integer> hotelInfoMap = getNowCreateSubHotelId();
            if(null != hotelInfoMap){
                int hotelId = hotelInfoMap.get("hotelId");
                int masterHotelId = hotelInfoMap.get("masterHotelId");

                String pro = (String)redisTemplate.opsForValue().get(SwitchHotelRoomStaticJobProcessIngRedisHead+masterHotelId+hotelId);
                if(null != pro){
                    log.info("Switch Hotel Room Static Job {} >>>>>>>> 携程静态物理房型,,当前酒店已存在在执行的线程，跳过: {}，{}",timestamp,pro,SwitchHotelRoomStaticJobProcessIngRedisHead+masterHotelId+hotelId);
                }else{
                    //当前流程标识
                    redisTemplate.opsForValue().set(SwitchHotelRoomStaticJobProcessIngRedisHead+masterHotelId+hotelId,"1",5L, TimeUnit.SECONDS);
                    requestHotelRoomInfoByHotelId(roomMethodGetUrl,hotelId,masterHotelId);
                }

            }else{
                //opsValueList返回空 则无新创建子酒店需处理
                log.info("Switch Hotel Room Static Job {} >>>>>>>> 携程静态物理房型 无新创建子酒店需处理,opsValueList返回空 {}", timestamp, DateUtil.now());
                //最后一次调用缓存保存
                checkSaveRedisData(saveUrl,true);
                //endingJob(true);
                //改版 2020-11-16  不停止定时器，人为判断是否需要停止
                log.info("Switch Hotel Room Static Job {} >>>>>>>> 拉取静态物理房型，未获取到缓存队列中的新创建子酒店数据，当前批次定时器跳过，人为判断是否需要关闭定时器: {}",timestamp, DateUtil.now());
            }
        }
        log.info("Switch Hotel Room Static Job {} >>>>>>>> 携程静态物理房型结束时间: {}",timestamp, DateUtil.now());
    }

    public Map<String,Integer> getNowCreateSubHotelId(){
        Long size = redisTemplate.opsForList().size(SwitchConstants.CREATE_SUB_FOR_ROOM_STATIC_JOB);
        if(size==0){
            log.info("Switch Hotel Room Static Job {} >>>>>>>> 携程静态物理房型 redisTemplate.opsForList().size = 0", timestamp);
            return null;
        }

        //获取当前最近创建的子酒店的id
        Map<String,Integer> map = null;
        String ids = (String)redisTemplate.opsForList().rightPop(SwitchConstants.CREATE_SUB_FOR_ROOM_STATIC_JOB);//masterHotelId+"_"+subHotelId

        if(!ObjectUtils.isEmpty(ids)){
            map = new HashMap();
            map.put("masterHotelId",Integer.valueOf(ids.split("_")[0]));
            map.put("hotelId",Integer.valueOf(ids.split("_")[1]));
        }
        log.info("Switch Hotel Room Static Job {} >>>>>>>> 携程静态物理房型 getNowCreateSubHotelId,time={},size={},ids={},map={}", timestamp, DateUtil.now(),size,ids, JSON.toJSONString(map));
        return map;
    }

    /**
     * 校验是否需要保存缓存的数据
     * @param saveUrl
     * @return
     */
    private boolean checkSaveRedisData(String saveUrl,boolean go) {
        log.info("Switchs Basic Room Type Job {} 携程静态物理房型定时器进入方法 checkSaveRedisData",timestamp);
        Map<String,List<RoomStaticInfoVo>> getInfos = getRedisWaitData();
        if(go){
            boolean result = requestSaveWaitDataApi(saveUrl,getInfos);
            return result;
        }else{
            if(null != getInfos && getInfos.size()>=thisRedisCount){
                //缓存酒店数据达到大于等于thisRedisCount，则执行保存到数据库操作，可能大于thisRedisCount比如数据操作保存失败还原加入到redis
                boolean result = requestSaveWaitDataApi(saveUrl,getInfos);
                return result;
            }else{
                return false;
            }
        }
    }
    /**
     * 调用保存缓存数据集合接口
     * @param saveUrl
     * @param roomStaticInfoListMap
     * @return
     */
    private boolean requestSaveWaitDataApi(String saveUrl,Map<String,List<RoomStaticInfoVo>> roomStaticInfoListMap) {
        if(null == roomStaticInfoListMap || roomStaticInfoListMap.size()==0){
            log.warn("Switchs Basic Room Type Job {} 携程静态物理房型定时器进入方法 requestSaveWaitDataApi 待保存数据redisWaitDataSaveMap为空",timestamp);
            return true;
        }
        log.info("Switchs Basic Room Type Job {} 携程静态物理房型定时器进入方法 requestSaveWaitDataApi",timestamp);
        Map map = new HashMap();
        map.put("roomStaticInfoListMap",roomStaticInfoListMap);
        //调用前,删除当前缓存的酒店数据，让下一次定时器正常查询上游接口。若调用结果失败则还原加入缓存
        deleteRedisWaitSaveData();
        //发起请求
        Result response = JobRestTemplateUtils.post(saveUrl, JSON.toJSONString(map),restTemplate);
        if(response.getCode()== CommonConstants.SUCCESS){
            log.info("Switchs Basic Room Type Job {} requestSaveWaitDataApi 保存数据成功",timestamp);
            return true;
        }else{
            log.error("Switchs Basic Room Type Job {} requestSaveWaitDataApi 返回失败: {}",timestamp,response);
            backPutRedisWaitSaveData(roomStaticInfoListMap);
            return false;
        }
    }

    /**
     * 还原追加回去待保存数据
     * @return
     */
    public void backPutRedisWaitSaveData(Map<String,List<RoomStaticInfoVo>> redisWaitDataMap){
        log.info("Switchs Basic Room Type Job {} 携程静态物理房型定时器进入方法 backPutRedisWaitSaveData 还原追加当前酒店信息缓存 ",timestamp);
        //还原redis数据
        Map<String,List<RoomStaticInfoVo>> getRedisMap = (Map<String,List<RoomStaticInfoVo>>)redisService.get(redisWaitSaveKey);
        if(null != getRedisMap && getRedisMap.size()>0){
            getRedisMap.putAll(redisWaitDataMap);
        }else{
            getRedisMap = redisWaitDataMap;
        }
        redisService.set(redisWaitSaveKey,getRedisMap);
    }

    /**
     * 请求静态物理房型接口
     * @param url
     * @param hotelId
     */
    private void requestHotelRoomInfoByHotelId(String url,int hotelId,int masterHotelId) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("hotelId", hotelId);
        params.put("masterHotelId", masterHotelId);
//        params.put("access_token",token);
        log.info("Switch Hotel Room Static Job 携程静态物理房型进入方法 requestHotelRoomInfoByHotelId: {},{}", JSON.toJSONString(params));
        //发起请求
        Result response = JobRestTemplateUtils.get(url,params,restTemplate);

        int er = 0;

        if(response.getCode()== CommonConstants.SUCCESS){
            if(null != response.getData()){
                //解析响应结果
                String datajson = JSON.toJSONString(response.getData());
                List<RoomStaticInfoVo> list = (List<RoomStaticInfoVo>) JSON.parseObject(datajson, List.class);
                if(null != list && list.size()>0){
                    //有数据，保存到redis待提交保存
                    //查询到数据，存入redis
                    Map<String,List<RoomStaticInfoVo>> dataMap = new HashMap();
                    dataMap.put(String.valueOf(hotelId)+","+masterHotelId,list);
                    boolean setResultRedis = setPutRedisWaitData(dataMap);
                    if(setResultRedis){
                        //successUpdateDeleteRedisHasCityMap(url);

                    }else{
                        //removeHasIdsMap(String.valueOf(hotelId));
                        //还原下次继续操作
                        redisTemplate.opsForList().leftPush(SwitchConstants.CREATE_SUB_FOR_ROOM_STATIC_JOB,masterHotelId+"_"+hotelId);

                        log.info("Switchs Basic room Job 该酒店物理房型 savePutRedisWaitData 异常: {},{}",response, JSON.toJSONString(params));
                    }
                }else{
                    //无数据或小于pageSize，当前子酒店无物理房型，跳过，更新下一个子酒店
                    log.warn("Switchs Basic room Job 该静态物理房型数据为空: {},{}",response, JSON.toJSONString(params));
                    //successUpdateDeleteRedisHasCityMap(url);
                }
            }else{
                //可能为空，正常执行，下一个子酒店
                log.warn("Switchs Basic room Job 该静态物理房型数据为空: {},{}",response, JSON.toJSONString(params));
                //successUpdateDeleteRedisHasCityMap(url);
            }
        }else{
            //removeHasIdsMap(String.valueOf(hotelId));
            //还原下次继续操作
            redisTemplate.opsForList().leftPush(SwitchConstants.CREATE_SUB_FOR_ROOM_STATIC_JOB,masterHotelId+"_"+hotelId);
            log.error("Switchs Basic room Job 获取携程静态物理房型信息异常错误: {},{}",response, JSON.toJSONString(params));
        }
        //释放流程标识
        redisTemplate.delete(SwitchHotelRoomStaticJobProcessIngRedisHead+masterHotelId+hotelId);
    }
    /**
     * 成功请求静态物理房型后，合法酒店房型数据，追加进缓存中
     */
    private boolean setPutRedisWaitData(Map<String, List<RoomStaticInfoVo>> voMap) {
        Map<String,List<RoomStaticInfoVo>> getRedisMap = (Map<String,List<RoomStaticInfoVo>>)redisService.get(redisWaitSaveKey);
        if(null != getRedisMap && getRedisMap.size()>0){
            getRedisMap.putAll(voMap);
        }else{
            getRedisMap = voMap;
        }
        boolean result = redisService.set(redisWaitSaveKey,getRedisMap);
        return result;
    }
    /**
     * 结束定时器
     */
    public void endingJob(boolean delKey){

        deleteRedisHasChildHotelIdsMap();
        deleteRedisChildHotelIdNow();

        //已最后一页，暂停定时器,删除rediskey
        /*try {

        version qyy 先不做暂停

            Thread.sleep(2500);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        JobForm jobForm = new JobForm();
        jobForm.setJobClassName(jobClassName);
        jobForm.setJobGroupName(jobGroupName);
        try {
            jobAndTriggerService.pauseJob(jobForm);
            log.warn("Switch Hotel Room Static Job 执行暂停！: {},{}",jobGroupName,jobClassName);
            if(delKey){
                deleteRedisHasChildHotelIdsMap();
                deleteRedisChildHotelIdNow();
            }
            log.info("Switchs Basic room Job {} 获取携程静态物理房型信息 暂停定时器 成功",timestamp);
        } catch (SchedulerException e) {
            log.error(e.getMessage(),e);
        }*/
    }

    public void deleteRedisChildHotelIdNow(){
        redisTemplate.delete(timestamp+redisSwHotelChildIdNow);
    }

    public void deleteRedisHasChildHotelIdsMap(){
        redisTemplate.delete(redisSwHotelChildIdsHas);
    }
    /**
     * 删除缓存的待保存数据
     * @return
     */
    public void deleteRedisWaitSaveData(){
        log.info("Switchs Basic Room Type Job {} 携程静态物理房型定时器进入方法 deleteRedisWaitSaveData 删除当前携程静态物理房型定时器数据缓存",timestamp);
        redisTemplate.delete(redisWaitSaveKey);
    }
    /**
     * 获取保存在缓存中的待save的数据
     */
    private Map<String,List<RoomStaticInfoVo>> getRedisWaitData() {
        return (Map<String,List<RoomStaticInfoVo>>)redisService.get(redisWaitSaveKey);
    }

}
